using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace AlfarEngine
{
    /// <summary>
    /// The Main class that encapsulate all the information and operation of the Engine.
    /// </summary>
    public class Engine
    {
        #region Variables

        static private int mMaxLayer = 4;

        /// <summary>
        /// Layers, 0 is the upper
        /// </summary>
        static private GameObject[] mBackgroundRoot = new GameObject[mMaxLayer];
        static private GameObject mDynamicRoot = new GameObject("DynamicRoot");


        static private GraphicsDeviceManager mGraphics;
        static private Game mGame;
        static private SpriteBatch mSpriteBatch;
        static private ContentManager mContent;
        static private ContentManager mInternalContent;

        static private RenderTarget2D mAlbedoBuffer;
        static private RenderTarget2D mShadowBuffer;

        static private Rectangle mCurrentViewPort = Rectangle.Empty;

        static private bool mShadowPass;
        static private float mAmbientLight = 0;

        static private bool mDrawCursor = false;
        static private Data.ReferencedTexture mCursorTexture = null;


        #endregion

        //----------------------------------------------------

        #region Accessors

        /// <summary>
        /// Return the array of root of the engine. 0 is the upper. maxLayer is the lower layer
        /// </summary>
        static public GameObject[] root { get { return mBackgroundRoot; } }

        /// <summary>
        /// Array in which should go all dynamic object, this will be updated even if not inside viewport
        /// </summary>
        static public GameObject dynamicRoot { get { return mDynamicRoot; } }

        static public ContentManager Content { get { return mContent; } }
        static public SpriteBatch spriteBatch { get { return mSpriteBatch; } }
        static public Game game { get { return mGame; } }

        static public int maxLayer { get { return mMaxLayer; } }

        static public Vector2 resolution { get { return new Vector2(mGraphics.PreferredBackBufferWidth, mGraphics.PreferredBackBufferHeight); } }

        /// <summary>
        /// set this to true to draw the cursor. If no texture are set (see setCursorTexture) it will simply show the defaut system cursor
        /// </summary>
        static public bool cursor { get { return mDrawCursor; } set { mDrawCursor = value; if (mCursorTexture == null) mGame.IsMouseVisible = value; } }

        /// <summary>
        /// the ambient light factor. 0 mean no ambient light (dark) 1 mean full ambient. Default is 0
        /// </summary>
        static public float ambientLight { get { return mAmbientLight; } set { mAmbientLight = value; if (mAmbientLight > 1) mAmbientLight = 1; } }

        /// <summary>
        /// The current viewport used for rendering. (IE : set to cam when rendering the scene, to light Range when rendering shadowmap etc...)
        /// </summary>
        static internal Rectangle currentViewport { get { return mCurrentViewPort; } set { mCurrentViewPort = value; } }
        /// <summary>
        /// Get the internal content manager, loading from the EmbededContent
        /// </summary>
        static internal ContentManager internalContentManager { get { return mInternalContent; } }

        /// <summary>
        /// tell if this is a shadowpass or not (usefull for not drawing not shadow caster)
        /// </summary>
        static internal bool shadowPass { get { return mShadowPass; } }

        #endregion

        //----------------------------------------------------

        #region Methods

        /// <summary>
        /// Initialize the engine
        /// </summary>
        /// <param name="pGame">The Game class you use the engine in</param>
        /// <param name="pManager">The Graphics Manager constructed in your game class</param>
        static public void Initialize(Game pGame, GraphicsDeviceManager pManager)
        {
            mGraphics = pManager;
            mGame = pGame;
            mContent = mGame.Content;
            mSpriteBatch = new SpriteBatch(mGame.GraphicsDevice);

            mInternalContent = new ResourceContentManager(game.Services, EmbededContent.ResourceManager);

            mAlbedoBuffer = new RenderTarget2D(mGame.GraphicsDevice, mGraphics.PreferredBackBufferWidth, mGraphics.PreferredBackBufferHeight);
            mShadowBuffer = new RenderTarget2D(mGame.GraphicsDevice, mGraphics.PreferredBackBufferWidth, mGraphics.PreferredBackBufferHeight);

            Utilities.Debug.Initialize();

            for (int i = 0; i < mMaxLayer; i++)
            {
                root[i] = new GameObject("Layer" + i);
            }

            Utilities.Debug.debug = true;
            mShadowPass = false;

            mAmbientLight = 0;
        }

        /// <summary>
        /// Update all the state of the Engine (Animations, physics, movement etc...)
        /// </summary>
        /// <param name="pTime"></param>
        static public void Update(GameTime pTime)
        {
            for (int i = mMaxLayer - 1; i >= 0; i--)
            {
                mBackgroundRoot[i].Update(pTime);
            }

            mDynamicRoot.Update(pTime);
        }

        /// <summary>
        /// Draw the Cameras viewports onto the backbuffer.
        /// </summary>
        /// <param name="pTime"></param>
        static public void Draw(GameTime pTime)
        {
            //Set the viewport to the camera viewport.
            mCurrentViewPort = new Rectangle((int)Components.Camera.mainCamera.upperCorner.X,
                                             (int)Components.Camera.mainCamera.upperCorner.Y,
                                             (int)resolution.X,
                                             (int)resolution.Y);

            //Compute shadowMap
            mShadowPass = true;
            Components.Light.RenderShadows(pTime);
            mShadowPass = false;

            //Set the albedo buffer as rendering target
            mGame.GraphicsDevice.SetRenderTarget(mAlbedoBuffer);

            Utilities.Debug.clearDebugLog();
            Utilities.Debug.numberOfSpriteDraw = 0;

            //clear the albedo buffer
            mGame.GraphicsDevice.Clear(Color.Black);

            mSpriteBatch.Begin();

            for (int i = mMaxLayer - 1; i >= 0; i--)
            {
                mBackgroundRoot[i].Draw(pTime);
                if (i == 1)
                {//shadow caster AND dynamic 
                    mDynamicRoot.Draw(pTime);
                }
            }

            mSpriteBatch.End();

            mGame.GraphicsDevice.SetRenderTarget(mShadowBuffer);
            mGame.GraphicsDevice.Clear(Color.White * (mAmbientLight * 0.5f));

            Components.Light.DrawShadow();

            Utilities.Debug.outputDebug("Number Of Sprite Drawned : " + Utilities.Debug.numberOfSpriteDraw);

            // ---- drawing the albedo buffer onto the back buffer

            mGame.GraphicsDevice.SetRenderTarget(null);
            mGame.GraphicsDevice.Clear(Color.Black);

            BlendState blendState = new BlendState();
            blendState.ColorSourceBlend = Blend.DestinationColor;
            blendState.ColorDestinationBlend = Blend.SourceColor;

            
            mSpriteBatch.Begin();
            mSpriteBatch.Draw((Texture2D)mAlbedoBuffer, Vector2.Zero, Color.White);
            mSpriteBatch.End();

            mSpriteBatch.Begin(SpriteSortMode.Deferred, blendState);
            mSpriteBatch.Draw((Texture2D)mShadowBuffer, Vector2.Zero, Color.White);
            mSpriteBatch.End();

            /*mSpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
            Components.Collider.DrawGrid();
            mSpriteBatch.End();*/
            
        }

        #endregion
    }
}
